﻿using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;

namespace NetFrame
{
    /// <summary>
    /// 用户连接信息对象
    /// </summary>
    public class UserToken
    {
        public delegate void SendProcessDelegate(SocketAsyncEventArgs e);
        public event SendProcessDelegate SendProcessEvent;

        public delegate void CloseProcessDelegate(UserToken token, string error);
        public event CloseProcessDelegate CloseProcessEvent;

        /// <summary>
        /// 用户连接
        /// </summary>
        public Socket Conn { get; set; }
        /// <summary>
        /// 用户异步接收网络数据对象
        /// </summary>
        public SocketAsyncEventArgs ReceiveSAEA;
        /// <summary>
        /// 用户异步发送网络数据对象
        /// </summary>
        public SocketAsyncEventArgs SendSAEA;

        public LengthEncode LengthEncode;
        public LengthDecode LengthDecode;
        public Encode Encode;
        public Decode Decode;

        public AbsHandlerCenter HandlerCenter;

        private List<byte> cache = new List<byte>();

        private bool isReading = false;
        private bool isWriting = false;
        Queue<byte[]> writeQueue = new Queue<byte[]>();

        public UserToken()
        {
            ReceiveSAEA = new SocketAsyncEventArgs();
            SendSAEA = new SocketAsyncEventArgs();
            ReceiveSAEA.UserToken = this;
            SendSAEA.UserToken = this;
            //设置接收对象的缓冲区大小
            ReceiveSAEA.SetBuffer(new byte[1024], 0, 1024);
        }

        /// <summary>
        /// 网络消息到达
        /// </summary>
        /// <param name="buff"></param>
        public void Receive(byte[] buff)
        {
            // 将消息写入缓存
            cache.AddRange(buff);
            if (!isReading)
            {
                isReading = true;
                OnData();
            }
        }

        public void Write(byte[] value)
        {
            if (Conn == null)
            {
                // 此连接已经断开了
                CloseProcessEvent(this, "调用已经断开的连接");
                return;
            }
            writeQueue.Enqueue(value);
            if (!isWriting)
            {
                isWriting = true;
                OnWrite();
            }
        }

        public void Writed()
        {
            //与OnData尾递归同理
            OnWrite();
        }

        public void Close()
        {
            try
            {
                writeQueue.Clear();
                cache.Clear();
                isReading = false;
                isWriting = false;
                Conn.Shutdown(SocketShutdown.Both);
                Conn.Close();
                Conn = null;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        /// <summary>
        /// 缓存中有数据处理
        /// </summary>
        private void OnData()
        {
            // 解码消息存储对象
            byte[] buff = null;
            // 当粘包解码器存在的时候 进行粘包处理
            if (LengthDecode != null)
            {
                // cache的数据在LengthDecode中边读边减少
                buff = LengthDecode(ref cache);
                // 消息未接收全 退出数据处理 等待下次消息到达
                if (buff == null)
                {
                    isReading = false;
                    return;
                }
            }
            else
            {
                // 缓存区中没有数据 直接跳出数据处理 等待下次消息到达
                if (cache.Count == 0)
                {
                    isReading = false;
                    return;
                }
                buff = cache.ToArray();
                cache.Clear();
            }
            // 反序列化方法是否存在
            if (Decode == null)
            {
                throw new Exception("message decode process is null");
            }
            // 进行消息反序列化
            object message = Decode(buff);

            // 通知应用层 有消息到达
            HandlerCenter.MessageReceive(this, message);

            // 尾递归 防止在消息处理过程中 有其他消息到达而没有经过处理
            OnData();
        }

        private void OnWrite()
        {
            // 判断发送消息队列是否有消息
            if (writeQueue.Count == 0)
            {
                isWriting = false;
                return;
            }

            // 取出第一条待发消息
            byte[] buff = writeQueue.Dequeue();

            // 设置消息发送异步对象的发送数据缓冲区数据
            SendSAEA.SetBuffer(buff, 0, buff.Length);
            // 开启异步发送
            bool result = Conn.SendAsync(SendSAEA);
            // 是否挂起
            if (!result)
            {
                SendProcessEvent(SendSAEA);
            }
        }
    }
}
